from flask_redis import FlaskRedis
import json
import logging
from datetime import timedelta, datetime
import time
from functools import wraps
import threading
from collections import OrderedDict

logger = logging.getLogger(__name__)

# 创建Redis客户端实例
redis_client = FlaskRedis()

class MemoryCache:
    """内存缓存实现，支持容量限制和LRU淘汰"""
    def __init__(self, max_size=1000):
        self.max_size = max_size
        self.cache = OrderedDict()  # 使用OrderedDict实现LRU
        self.lock = threading.Lock()
        self.size = 0
        
    def set(self, key, value, expire):
        """设置缓存值
        
        Args:
            key: 缓存键
            value: 缓存值
            expire: 过期时间（秒）
        """
        with self.lock:
            # 如果键已存在，先删除旧值
            if key in self.cache:
                self.size -= 1
                
            # 如果达到容量限制，删除最旧的项
            while self.size >= self.max_size:
                self._remove_oldest()
                
            self.cache[key] = {
                'data': value,
                'expire_at': time.time() + expire,
                'access_time': time.time()
            }
            self.size += 1
            
    def get(self, key):
        """获取缓存值
        
        Args:
            key: 缓存键
            
        Returns:
            缓存值，如果不存在或已过期则返回None
        """
        with self.lock:
            if key not in self.cache:
                return None
                
            item = self.cache[key]
            current_time = time.time()
            
            # 检查是否过期
            if current_time > item['expire_at']:
                del self.cache[key]
                self.size -= 1
                return None
                
            # 更新访问时间
            item['access_time'] = current_time
            # 移动到最新位置
            self.cache.move_to_end(key)
            return item['data']
            
    def _remove_oldest(self):
        """删除最旧的缓存项"""
        if self.cache:
            # 找到最旧的项（访问时间最早的）
            oldest_key = min(self.cache.items(), 
                           key=lambda x: x[1]['access_time'])[0]
            del self.cache[oldest_key]
            self.size -= 1
            
    def clear(self):
        """清空缓存"""
        with self.lock:
            self.cache.clear()
            self.size = 0
            
    def remove_expired(self):
        """删除所有过期的缓存项"""
        with self.lock:
            current_time = time.time()
            expired_keys = [k for k, v in self.cache.items() 
                          if current_time > v['expire_at']]
            for key in expired_keys:
                del self.cache[key]
                self.size -= 1
                
    def get_size(self):
        """获取当前缓存大小"""
        return self.size

# 创建内存缓存实例
memory_cache = MemoryCache(max_size=1000)  # 最多存储1000个缓存项
memory_cache_lock = threading.Lock()

# Redis连接状态
redis_available = True

def check_redis_connection():
    """检查Redis连接状态"""
    global redis_available
    try:
        redis_client.ping()
        redis_available = True
        return True
    except Exception as e:
        redis_available = False
        logger.error(f"Redis连接失败: {str(e)}")
        return False

def with_fallback(func):
    """缓存降级装饰器"""
    @wraps(func)
    def wrapper(*args, **kwargs):
        if not redis_available and not check_redis_connection():
            # 如果Redis不可用，使用内存缓存
            logger.warning("Redis不可用，使用内存缓存降级")
            return func(*args, **kwargs, use_memory_cache=True)
        return func(*args, **kwargs)
    return wrapper

def init_cache(app):
    """初始化Redis缓存"""
    try:
        redis_client.init_app(app)
        if check_redis_connection():
            logger.info("Redis cache initialized successfully")
        else:
            logger.warning("Redis不可用，将使用内存缓存降级")
    except Exception as e:
        logger.error(f"Failed to initialize Redis cache: {str(e)}")
        raise

@with_fallback
def cache_gesture_data(gesture_id, data, expire=3600, use_memory_cache=False):
    """缓存手势数据
    
    Args:
        gesture_id: 手势ID
        data: 手势数据
        expire: 过期时间（秒），默认1小时
        use_memory_cache: 是否使用内存缓存
    """
    try:
        key = f'gesture:{gesture_id}'
        # 确保数据包含时间戳
        if 'timestamp' not in data:
            data['timestamp'] = datetime.now().isoformat()
        
        if use_memory_cache:
            memory_cache.set(key, data, expire)
            logger.debug(f"Cached gesture data in memory for gesture {gesture_id}")
        else:
            redis_client.set(key, json.dumps(data), ex=expire)
            logger.debug(f"Cached gesture data in Redis for gesture {gesture_id}")
    except Exception as e:
        logger.error(f"Failed to cache gesture data: {str(e)}")

@with_fallback
def get_cached_gesture_data(gesture_id, use_memory_cache=False):
    """获取缓存的手势数据
    
    Args:
        gesture_id: 手势ID
        use_memory_cache: 是否使用内存缓存
    
    Returns:
        手势数据，如果不存在或已过期则返回None
    """
    try:
        key = f'gesture:{gesture_id}'
        
        if use_memory_cache:
            data = memory_cache.get(key)
            if data:
                # 检查时间戳是否过期（超过1小时）
                if 'timestamp' in data:
                    try:
                        timestamp = datetime.fromisoformat(data['timestamp'])
                        if (datetime.now() - timestamp).total_seconds() > 3600:
                            logger.debug(f"Cache data for gesture {gesture_id} has expired")
                            return None
                    except ValueError:
                        logger.error(f"Invalid timestamp format in cache for gesture {gesture_id}")
                        return None
                return data
            return None
        
        data = redis_client.get(key)
        if data:
            data = json.loads(data)
            # 检查时间戳是否过期（超过1小时）
            if 'timestamp' in data:
                try:
                    timestamp = datetime.fromisoformat(data['timestamp'])
                    if (datetime.now() - timestamp).total_seconds() > 3600:
                        logger.debug(f"Cache data for gesture {gesture_id} has expired")
                        return None
                except ValueError:
                    logger.error(f"Invalid timestamp format in cache for gesture {gesture_id}")
                    return None
            return data
        return None
    except Exception as e:
        logger.error(f"Failed to get cached gesture data: {str(e)}")
        return None

@with_fallback
def cache_user_session(user_id, session_data, expire=3600, use_memory_cache=False):
    """缓存用户会话数据
    
    Args:
        user_id: 用户ID
        session_data: 会话数据
        expire: 过期时间（秒），默认1小时
        use_memory_cache: 是否使用内存缓存
    """
    try:
        key = f'user_session:{user_id}'
        if use_memory_cache:
            memory_cache.set(key, session_data, expire)
            logger.debug(f"Cached user session in memory for user {user_id}")
        else:
            redis_client.set(key, json.dumps(session_data), ex=expire)
            logger.debug(f"Cached user session in Redis for user {user_id}")
    except Exception as e:
        logger.error(f"Failed to cache user session: {str(e)}")

@with_fallback
def get_cached_user_session(user_id, use_memory_cache=False):
    """获取缓存的用户会话数据
    
    Args:
        user_id: 用户ID
        use_memory_cache: 是否使用内存缓存
    
    Returns:
        会话数据，如果不存在则返回None
    """
    try:
        key = f'user_session:{user_id}'
        if use_memory_cache:
            return memory_cache.get(key)
        
        data = redis_client.get(key)
        return json.loads(data) if data else None
    except Exception as e:
        logger.error(f"Failed to get cached user session: {str(e)}")
        return None

@with_fallback
def cache_gesture_contents(contents, expire=3600, use_memory_cache=False):
    """缓存手势指令内容
    
    Args:
        contents: 手势指令内容字典
        expire: 过期时间（秒），默认1小时
        use_memory_cache: 是否使用内存缓存
    """
    try:
        key = 'gesture_contents'
        if use_memory_cache:
            memory_cache.set(key, contents, expire)
            logger.debug("Cached gesture contents in memory")
        else:
            redis_client.set(key, json.dumps(contents), ex=expire)
            logger.debug("Cached gesture contents in Redis")
    except Exception as e:
        logger.error(f"Failed to cache gesture contents: {str(e)}")

@with_fallback
def get_cached_gesture_contents(use_memory_cache=False):
    """获取缓存的手势指令内容
    
    Args:
        use_memory_cache: 是否使用内存缓存
    
    Returns:
        手势指令内容字典，如果不存在则返回None
    """
    try:
        key = 'gesture_contents'
        if use_memory_cache:
            return memory_cache.get(key)
        
        data = redis_client.get(key)
        return json.loads(data) if data else None
    except Exception as e:
        logger.error(f"Failed to get cached gesture contents: {str(e)}")
        return None

@with_fallback
def delete_cache(key_pattern, use_memory_cache=False):
    """删除指定模式的缓存
    
    Args:
        key_pattern: 缓存键模式
        use_memory_cache: 是否使用内存缓存
    """
    try:
        if use_memory_cache:
            with memory_cache_lock:
                # 删除匹配的缓存项
                keys_to_delete = [k for k in memory_cache.cache.keys() 
                                if key_pattern in k]
                for k in keys_to_delete:
                    del memory_cache.cache[k]
                    memory_cache.size -= 1
                logger.debug(f"Deleted memory cache keys matching pattern: {key_pattern}")
        else:
            keys = redis_client.keys(key_pattern)
            if keys:
                redis_client.delete(*keys)
                logger.debug(f"Deleted Redis cache keys matching pattern: {key_pattern}")
    except Exception as e:
        logger.error(f"Failed to delete cache: {str(e)}")

@with_fallback
def clear_all_cache(use_memory_cache=False):
    """清除所有缓存
    
    Args:
        use_memory_cache: 是否使用内存缓存
    """
    try:
        if use_memory_cache:
            memory_cache.clear()
            logger.info("Cleared all memory cache")
        else:
            redis_client.flushall()
            logger.info("Cleared all Redis cache")
    except Exception as e:
        logger.error(f"Failed to clear cache: {str(e)}")

# 定期清理过期缓存
def cleanup_expired_cache():
    """清理过期的缓存项"""
    try:
        memory_cache.remove_expired()
    except Exception as e:
        logger.error(f"Failed to cleanup expired cache: {str(e)}")

# 启动定期清理任务
def start_cache_cleanup_task():
    """启动定期清理任务"""
    import threading
    def cleanup_task():
        while True:
            cleanup_expired_cache()
            time.sleep(300)  # 每5分钟清理一次
            
    cleanup_thread = threading.Thread(target=cleanup_task, daemon=True)
    cleanup_thread.start() 